package cn.mesmile.flow.config;

import cn.mesmile.flow.cache.MyCache;
import cn.mesmile.flow.listen.MyEventListener;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.spring.ProcessEngineFactoryBean;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zb
 * @date 2020/12/8 10:29
 * @Description
 */
@Configuration
public class MyFlowableConfig {


    @Bean
    public MyCache processDefinitionCache() {
        return new MyCache();
    }


//    @Bean
    public SpringProcessEngineConfiguration springProcessEngineConfiguration(){
        SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
        // 这里的配置和下面 不集成spring是一致的
        return configuration;
    }

//    @Bean
    public ProcessEngineFactoryBean processEngineFactoryBean(){
        ProcessEngineFactoryBean processEngineFactoryBean = new ProcessEngineFactoryBean();
        processEngineFactoryBean.setProcessEngineConfiguration(springProcessEngineConfiguration());
        return processEngineFactoryBean;
    }

    /**
     * 自定义流程引擎配置
     * mysql 5.6.4+: 支持毫秒精度。可以使用DDL文件（默认的包含mysql的文件）。可以使用自动创建/升级。
     *
     * @return
     */
//    @Bean
    public ProcessEngine processEngine() {
        List<FlowableEventListener> eventListeners = new ArrayList<>();
        eventListeners.add(new MyEventListener());

        // 流程引擎独立运行。Flowable自行处理事务。在默认情况下，
        // 数据库检查只在引擎启动时进行（如果Flowable表结构不存在或表结构版本不对，会抛出异常)
        ProcessEngineConfiguration configuration = ProcessEngineConfiguration
                .createStandaloneProcessEngineConfiguration();

        // 在流程引擎中配置的事件监听器会在流程引擎启动时生效，[引擎重启后也会保持有效]。
        configuration.setEventListeners(eventListeners);

        // 要在特定类型的事件分发时得到通知，使用typedEventListeners参数，值为map。
        // map的key为逗号分隔的事件名字列表（或者一个事件的名字）下面的代码片段在配置中添加了一个事件监听器，它会在作业执行成功或失败时得到通知：
//        Map<String, List<FlowableEventListener>> map = new HashMap<>();
//        map.put("JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE",eventListeners);
//        configuration.setTypedEventListeners(map);


        ProcessEngine processEngine = configuration.setDatabaseSchema(ProcessEngineConfiguration.DATABASE_TYPE_MYSQL)
                /*
                     DatabaseSchemaUpdate
                       false (默认): 当引擎启动时，检查数据库表结构的版本是否匹配库文件版本。版本不匹配时抛出异常。
                       true: 构建引擎时，检查并在需要时更新表结构。表结构不存在则会创建。
                       create-drop: 引擎创建时创建表结构，并在引擎关闭时删除表结构。
                 */
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE)
                .setJdbcDriver("")
                .setJdbcUrl("")
                .setJdbcUsername("")
                .setJdbcPassword("")
                // 连接池能够容纳的最大活动连接数量。默认值为10
                .setJdbcMaxActiveConnections(10)
                //  连接池能够容纳的最大空闲连接数量
                .setJdbcMaxIdleConnections(10)
                // 连接从连接池“取出”后，被强制返回前的最大时间间隔，单位为毫秒。默认值为20000（20秒）
                .setJdbcMaxCheckoutTime(20000)
                // 这是一个底层设置，在连接池获取连接的时间异常长时，
                // 打印日志并尝试重新获取连接（避免连接池配置错误，导致没有异常提示）。默认值为20000（20秒）
                .setJdbcMaxWaitTime(20000)
                .setAsyncExecutorActivate(false)
                .buildProcessEngine();

        return processEngine;
    }

//    如果在Java EE 7下运行，容器还可以使用符合JSR-236标准的ManagedAsyncJobExecutor来管理线程。
//    要启用这个功能，需要在配置中如下加入线程工厂
//    @Bean
//    public JndiObjectFactoryBean jndiObjectFactoryBean(){
//        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
//        jndiObjectFactoryBean.setJndiName("java:jboss/ee/concurrency/factory/default");
//        return jndiObjectFactoryBean;
//    }
//
//    @Bean
//    public ManagedAsyncJobExecutor managedAsyncJobExecutor(JndiObjectFactoryBean jndiObjectFactoryBean) {
//        ManagedAsyncJobExecutor managedAsyncJobExecutor = new ManagedAsyncJobExecutor();
//        managedAsyncJobExecutor.setThreadFactory(jndiObjectFactoryBean);
//        return managedAsyncJobExecutor;
//    }


    /**
     *  运行的时候添加 【监听器】

     可以使用API(RuntimeService)为引擎添加或删除事件监听器：
     新增一个监听器，会在所有事件发生时被通知。
      @param listenerToAdd 要新增的监听器
    void addEventListener(FlowableEventListener listenerToAdd);

     新增一个监听器，在给定类型的事件发生时被通知。
    @param listenerToAdd 要新增的监听器
    @param types 监听器需要监听的事件类型
    void addEventListener(FlowableEventListener listenerToAdd, FlowableEventType... types);

    从分发器中移除指定监听器。该监听器将不再被通知，无论该监听器注册为监听何种类型。
    @param listenerToRemove 要移除的监听器
    void removeEventListener(FlowableEventListener listenerToRemove);

     */

    /**
     * /////////////////////////////////////////////////////////////////////////////////////////////////
     *  第一个监听器接收任何类型的事件，使用完全限定类名定义。
     *   <flowable:eventListener class="org.flowable.engine.test.MyEventListener" />
     *
     *  第二个监听器只在作业成功执行或失败时被通知，使用流程引擎配置中beans参数定义的bean作为监听器
     *  <flowable:eventListener delegateExpression="${testEventListener}"
     *  events="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" />
     *
     *
     *
     * ////////////////////////////////////////////////////////////////////////////////////////////////
     * 实体相关的事件也可以在流程定义中增加监听器，只有在特定实体类型的事件发生时得到通知。
     * 下面的代码片段展示了如何设置。可以响应实体的所有事件（第一个例子），
     * 或只响应实体的特定类型事件（第二个例子）
     *  <flowable:eventListener class="org.flowable.engine.test.MyEventListener" entityType="task" />
     *
     *   <flowable:eventListener delegateExpression="${testEventListener}"
     *   events="ENTITY_CREATED" entityType="task" />
     *
     *   entityType可用的值有：attachment（附件）, comment（备注）, execution（执行）, identity-link（身份关联）
     *   , job（作业）, process-instance（流程实例）, process-definition（流程定义）, task（任务）
     *
     *
     * /////////////////////////////////////////////////////////////////////////////////////////////////
     *
     *
     *  可以通过API提供事件分发机制，向任何在引擎中注册的监听器分发自定义事件。
     *  建议（但不强制）只分发CUSTOM类型的FlowableEvents。使用RuntimeService分发事件：
     *
     *  将给定事件分发给所有注册监听器。
     *  @param event 要分发的事件。
     *
     *  @throws FlowableException 当分发事件发生异常，或者{@link FlowableEventDispatcher}被禁用。
     *  @throws FlowableIllegalArgumentException 当给定事件不可分发
        void dispatchEvent(FlowableEvent event);
     *
     */

}
